/*
 * Copyright (c) zhg2yqq.com Corp.
 * All Rights Reserved.
 */
package com.zhg2yqq.wheels.job.util;

import java.io.Serializable;
import java.util.Objects;

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;

import com.zhg2yqq.wheels.job.constant.JobConstant;
import com.zhg2yqq.wheels.job.core.JobScheduleParameter;
import com.zhg2yqq.wheels.job.execution.QuartzDisallowConcurrentExecution;
import com.zhg2yqq.wheels.job.execution.QuartzJobExecution;

/**
 * @author zhg2yqq, 2023年2月17日
 * @version zhg2yqq v1.0
 */
public class ScheduleUtils {
    /**
     * 得到quartz任务类
     *
     * @param parameter 执行计划
     * @return 具体执行任务类
     */
    private static Class<? extends Job> getQuartzJobClass(JobScheduleParameter parameter) {
        return parameter.isConcurrent() ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class;
    }

    /**
     * 构建任务触发对象
     */
    public static TriggerKey getTriggerKey(Serializable jobId, String jobGroup) {
        return TriggerKey.triggerKey(JobConstant.TASK_CLASS_NAME + jobId, jobGroup);
    }

    /**
     * 构建任务键对象
     */
    public static JobKey getJobKey(Serializable jobId, String jobGroup) {
        return JobKey.jobKey(JobConstant.TASK_CLASS_NAME + jobId, jobGroup);
    }

    /**
     * 创建定时任务
     */
    public static void createScheduleJob(Scheduler scheduler, JobScheduleParameter parameter)
            throws SchedulerException {
        Class<? extends Job> jobClass = getQuartzJobClass(parameter);
        // 构建job信息
        String jobId = parameter.getKey();
        String jobGroup = parameter.getGroup();
        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup))
                .build();

        // 表达式调度构建器
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder
                .cronSchedule(parameter.getCronExpression());
        cronScheduleBuilder = handleCronScheduleMisfirePolicy(parameter, cronScheduleBuilder);

        // 按新的cronExpression表达式构建一个新的trigger
        CronTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(getTriggerKey(jobId, jobGroup)).withSchedule(cronScheduleBuilder)
                .build();

        // 放入参数，运行时的方法可以获取
        jobDetail.getJobDataMap().put(JobConstant.TASK_PROPERTIES, parameter);

        // 判断是否存在
        if (scheduler.checkExists(getJobKey(jobId, jobGroup))) {
            // 防止创建时存在数据问题 先移除，然后在执行创建操作
            scheduler.deleteJob(getJobKey(jobId, jobGroup));
        }

        // 判断任务是否过期
        if (Objects.nonNull(CronUtils.getNextExecution(parameter.getCronExpression()))) {
            // 执行调度任务
            scheduler.scheduleJob(jobDetail, trigger);
        }

        // 暂停任务
        if (!JobConstant.Status.NORMAL.equals(parameter.getStatus())) {
            scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
    }

    /**
     * 设置定时任务策略
     */
    public static CronScheduleBuilder handleCronScheduleMisfirePolicy(JobScheduleParameter job,
                                                                      CronScheduleBuilder cb) {
        switch (job.getMisfirePolicy()) {
            case JobConstant.MISFIRE_DEFAULT:
                return cb;
            case JobConstant.MISFIRE_IGNORE_MISFIRES:
                return cb.withMisfireHandlingInstructionIgnoreMisfires();
            case JobConstant.MISFIRE_FIRE_AND_PROCEED:
                return cb.withMisfireHandlingInstructionFireAndProceed();
            case JobConstant.MISFIRE_DO_NOTHING:
                return cb.withMisfireHandlingInstructionDoNothing();
            default:
                throw new IllegalArgumentException("Job misfire way not defined");
        }
    }

//    /**
//     * 检查包名是否为白名单配置
//     * 
//     * @param invokeTarget 目标字符串
//     * @param whiteList 可调用白名单(类全路径或方法全路径，例如：com.zhg2yqq.wheels.system.util.ScheduleUtils,
//     *            com.zhg2yqq.wheels.system.util.ScheduleUtils.whiteList)，为null表示拒绝一切方法调用
//     * @return 结果
//     */
//    public static boolean whiteList(String invokeTarget, String... whiteList) {
//        if (whiteList == null) {
//            return false;
//        }
//        String methodName = CharSequenceUtil.subBefore(invokeTarget, "(", false);
//        int count = CharSequenceUtil.count(methodName, ".");
//        if (count > 1) {
//            // 类
//            return CharSequenceUtil.startWithAnyIgnoreCase(invokeTarget, whiteList);
//        }
//        // bean
//        String[] beanInfo = CharSequenceUtil.splitToArray(invokeTarget, ".");
//        String beanName = beanInfo[0];
//        Object obj = SpringUtil.getBean(beanName);
//        String beanClassName = obj.getClass().getName();
//        String beanTarget = beanClassName + "." + beanInfo[1];
//        return CharSequenceUtil.startWithAnyIgnoreCase(beanTarget, whiteList);
//    }
}
